/// <reference path = "./ShaderComponent.ts" />

module lcc {

export module render {

const {ccclass, property, menu } = cc._decorator;

//@ts-ignore
let gfx = cc.gfx;

let _temp_color = cc.color();

@ccclass("lcc.render.ShaderColor")
@menu("i18n:lcc-render.menu_component/ShaderColor")
export class ShaderColor extends ShaderComponent {

	_tag:string = "color";

	@property(VariableConfig)
	_colorVar:VariableConfig = new VariableConfig(
		true, VariableType.UNIFORM, 
		"ATTR_COLOR", false, "a_color", "UNIF_COLOR", false, "color");
	@property({
		type : VariableConfig,
		tooltip : "颜色变量"
	})
	get colorVar(){
		return this._colorVar;
	}
	set colorVar(value:VariableConfig){
		this._colorVar = value;
		this.onRenderUpdateMaterial();
		this.node.emit("shader_update_attribute");
		if(this.colorVar.type == VariableType.UNIFORM){
			this._colorType = ValueType.SINGLE;
		}
		//Editor.log("colorVar");
	}
	
	@property()
	_applyNodeColor:boolean = true;
	@property({
		tooltip : "应用节点的颜色"
	})
	get applyNodeColor(){
		return this._applyNodeColor;
	}
	set applyNodeColor(value:boolean){
		if(this._applyNodeColor != value){
			this._applyNodeColor = value;
			this.onNodeColorChanged();
			if(!value){
				this.checkVertexColors();
			}
			if(this._colorVar.type == VariableType.ATTRIBUTE){
				this.node.emit("shader_update_vertex");
			}else{
				//this.node.emit("shader_update_material");
				this.onRenderUpdateMaterial();
			}
		}
	}

	@property({
		visible (){
			return this._applyNodeColor;
		},
		readonly : true,
		tooltip : "节点的颜色",
		editorOnly : true,
	})
	nodeColor:cc.Color = cc.Color.WHITE;

	@property({
		type :cc.Enum(ValueType)
	})
	_colorType:ValueType = ValueType.SINGLE;
	@property({
		visible (){
			return !this._applyNodeColor && this._colorVar.type != VariableType.UNIFORM;
		},
		type :cc.Enum(ValueType),
		tooltip : "颜色类型"
	})
	get colorType(){
		return this._colorType;
	}
	set colorType(value:ValueType){
		if(this._colorType != value){
			this._colorType = value;
			this.checkVertexColors();
			if(this._colorVar.type == VariableType.ATTRIBUTE){
				this.node.emit("shader_update_vertex");
			}else{
				//this.node.emit("shader_update_material");
				this.onRenderUpdateMaterial();
			}
		}
	}

	@property(cc.Color)
	_color:cc.Color = cc.Color.WHITE;
	@property({
		visible (){
			return !this._applyNodeColor && this._colorType == ValueType.SINGLE;
		},
		type : cc.Color,
		tooltip : "颜色值"
	})
	get color(){
		return this._color;
	}
	set color(value:cc.Color){
		this._color = value;
		if(this._colorVar.type == VariableType.ATTRIBUTE){
			this.node.emit("shader_update_vertex");
		}else{
			//this.node.emit("shader_update_material");
			this.onRenderUpdateMaterial();
		}
	}

	@property([cc.Color])
	_vertexColors:cc.Color[] = [];
	@property({
		visible (){
			return !this._applyNodeColor && this._colorType == ValueType.VERTEX;
		},
		type : [cc.Color],
		tooltip : "顶点颜色"
	})
	get vertexColors(){
		return this._vertexColors;
	}
	set vertexColors(value:cc.Color[]){
		this._vertexColors = value;
		this.checkVertexColors();
		if(this._colorVar.type == VariableType.ATTRIBUTE){
			this.node.emit("shader_update_vertex");
		}else{
			//this.node.emit("shader_update_material");
			this.onRenderUpdateMaterial();
		}
	}

	/**
	 * 颜色偏移
	 */
    private _colorOffset:number = 0;

    onLoad(){
        this.node.on("render_shape_checked", this.checkVertexColors, this);
        this.node.on("render_update_material", this.onRenderUpdateMaterial, this);
		this.node.on("render_update_attribute", this.onRenderUpdateAttribute, this);
		this.node.on("render_update_vertex", this.onRenderUpdateRenderData, this);
		this.node.on(cc.Node.EventType.COLOR_CHANGED, this.onNodeColorChanged, this);
    }

    onDestroy(){
		this.node.targetOff(this);
    }
    
	onEnable(){
		this.onNodeColorChanged();
        this.onStateUpdate(true);
	}

	onDisable(){
        this.onStateUpdate(false);
    }

    private onStateUpdate(enable:boolean){
		this.node.emit("shader_update_tag");
		if(this._colorVar.type == VariableType.ATTRIBUTE){
			this.node.emit("shader_update_attribute");
		}else{
			//this.node.emit("shader_update_material");
			this.onRenderUpdateMaterial();
		}
		if(this._applyNodeColor){
			this.onNodeColorChanged();
		}else{
			this.checkVertexColors();
		}
	}
	
	/**
	 * 检查顶点颜色
	 */
	private checkVertexColors(){
		if(this.enabled){
			if(this._colorType == ValueType.VERTEX){
				let rcomp = this.getComponent(RenderSystem);
				if(rcomp){
					let colors = this._vertexColors;
					let vc = rcomp._verticesCount;
					colors.length = vc;
					for(let i = 0; i < vc; i++){
						let color = colors[i];
						if(color == null){
							colors[i] = cc.Color.WHITE;
						}
					}
				}
			}else{
				this._vertexColors = [];
			}
		}
	}
    
	private onNodeColorChanged(){
		if(this._applyNodeColor){
			if(CC_EDITOR){
				this.nodeColor = this.node.color;
			}
			if(this._colorVar.type == VariableType.ATTRIBUTE){
				this.node.emit("shader_update_vertex");
			}else{
				//this.node.emit("shader_update_material");
				this.onRenderUpdateMaterial();
			}
		}
	}

	private premultiplyAlpha(color:cc.Color){
		// @ts-ignore
		cc.Color.premultiplyAlpha(_temp_color, color);
		// @ts-ignore
		return _temp_color._val;
	}

	/**
	 * 获得属性颜色
	 * @param comp 
	 */
	private getAttributeColors(comp){
		let vc = comp._verticesCount;
		let premultiply = comp._srcBlendFactor === cc.macro.BlendFactor.ONE;
		let colors:number[] = [];
		if(this._applyNodeColor){
			// @ts-ignore
			let cval = premultiply ? this.premultiplyAlpha(this.node._color) : this.node._color._val;
			colors.push(cval);
		}else{
			if(this._colorType === ValueType.SINGLE){
				// @ts-ignore
				let cval = premultiply ? this.premultiplyAlpha(this._color) : this._color._val;
				colors.push(cval);
			}else{
				for (let i = 0; i < vc; i++){
					let c = this._vertexColors[i];
					// @ts-ignore
					colors.push(premultiply ? this.premultiplyAlpha(c) : c._val);
				}
			}
		}
		return colors;
	}

	private getUniformColor(comp){
		let premultiply = comp._srcBlendFactor === cc.macro.BlendFactor.ONE;
		let color:cc.Color = null;
		if(this._applyNodeColor){
			//@ts-ignore
			color = this.node._color;
		}else{
			color = this._color;
		}
		if(premultiply){
			// @ts-ignore
			cc.Color.premultiplyAlpha(_temp_color, color);
			color = _temp_color;
		}
		return new cc.Vec4(color.r / 255, color.g / 255, color.b / 255, color.a / 255);
	}

    private onRenderUpdateMaterial (comp?) {
		comp = comp || this.getComponent(cc.RenderComponent);
		if(comp){
			// make sure material is belong to self.
			let material = comp._materials[0];
			if (material) {
				if (this.checkMaterialMacro(material, this._colorVar.unifMacro)) {
					if(this.enabled && this._colorVar.type === VariableType.UNIFORM){
						this.defineMaterialMacro(material, this._colorVar.unifMacro, true);
						material.setProperty(this._colorVar.unifName, this.getUniformColor(comp));
						//Editor.log("onRenderUpdateMaterial", this.getUniformColor(comp));
					}else{
						this.defineMaterialMacro(material, this._colorVar.unifMacro, false);
					}
					//Editor.log("onRenderUpdateMaterial", this.enabled);
				}
			}
		}
    }
	
	private onRenderUpdateAttribute(comp:RenderSystem){
		// @ts-ignore
		let material = comp._materials[0];
		if (material) {
			if (this.checkMaterialMacro(material, this._colorVar.attrMacro)) {
				if(this.enabled && this._colorVar.type === VariableType.ATTRIBUTE){
					this.defineMaterialMacro(material, this._colorVar.attrMacro, true);
					this._colorOffset = comp.addVertexAttribute({ 
						name: this._colorVar.attrName, 
						type: gfx.ATTR_TYPE_UINT8, 
						num: 4, 
						normalize: true 
					}, 1);
					//Editor.log("onRenderUpdateAttribute", this._colorOffset);
				}else{
					this.defineMaterialMacro(material, this._colorVar.attrMacro, false);
					this._colorOffset = 0;
				}
				//Editor.log("onRenderUpdateAttribute", this.enabled);
			}
		}
	}

	private onRenderUpdateRenderData(comp){
        if(this.enabled){
			//Editor.log("onRenderUpdateRenderData");
			if(this._colorOffset > 0){
				let assembler = comp._assembler;
				let uintVerts = assembler.renderData.uintVDatas[0];
				if (uintVerts) {
					let colors = this.getAttributeColors(comp);
					let clen = colors.length;
					let colorOffset = this._colorOffset;
					let floatsPerVert = assembler.floatsPerVert;
					for (let i = colorOffset, l = uintVerts.length, j = 0; i < l; i += floatsPerVert, j++) {
						uintVerts[i] = j < clen ? colors[j] : colors[0];
					}
				}
			}
        }
	}
}

}

}
